"use client"; import { useRouter } from "next/navigation"; import { useEffect, useMemo, useState } from "react"; import { DotsLoader } from "@/components/Componentes/button"; import NavigationButton from "@/components/Componentes/navigation-button"; import { PageBackground } from "@/components/Componentes/page-background"; import { hasQuestionAnswerValue, QuestionAnswersProvider, useQuestionAnswers, } from "@/components/Componentes/question-answer-storage"; import QuestionExitNavigationButton from "@/components/Componentes/question-exit-navigation-button"; import QuestionRenderer from "@/components/Componentes/question-renderer"; import QuestionSectionFlow from "@/components/Componentes/question-section-flow"; import StickyHeader from "@/components/Componentes/sticky-header"; import TestIntroPage from "@/components/Componentes/test-intro-page"; import TestQuestionsFlow, { type TestQuestion, } from "@/components/Componentes/test-questions-flow"; import { cattellFallbackQuestions } from "@/data/cattell-fallback"; import { glasserFallbackQuestions } from "@/data/glasser-fallback"; import { getQuestionListItemBySlug, isQuestionListItemVisibleForProfile, isQuestionRequiredForProfile, isQuestionVisibleForProfile, type QuestionField, } from "@/data/question-data"; import type { MarriageGender } from "@/hooks/marriage/types"; import { useCattellQuestionsQuery, useSubmitCattellAssessmentMutation, } from "@/hooks/marriage/use-cattell"; import { useGlasserQuestionsQuery, useSubmitGlasserAssessmentMutation, } from "@/hooks/marriage/use-glasser"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; import { defaultLocale, type Locale } from "@/translations/config"; import { useI18n } from "@/translations/provider"; import AnswerPaceSheet from "./answer-pace-sheet"; type QuestionDetailClientProps = { closeLabel: string; continueLabel: string; description: string; informationLabel: string; itemSlug: string; locale?: Locale; questionsListHref: string; title: string; }; type StoredQuestionField = { label?: string; value?: unknown; type?: string; key?: string; }; type StoredAnswers = { fields?: StoredQuestionField[]; }; function getQuestionStorageKey(slug: string) { return `marriage:sections:${slug}:answers`; } function parseStoredAge(value: unknown) { if (typeof value === "number" && Number.isFinite(value)) { return value; } if (typeof value === "string") { const trimmedValue = value.trim(); if (!trimmedValue) { return null; } const numericAge = Number(trimmedValue); if (Number.isFinite(numericAge)) { return numericAge; } const dateOfBirth = new Date(trimmedValue); if (Number.isNaN(dateOfBirth.getTime())) { return null; } const today = new Date(); let age = today.getFullYear() - dateOfBirth.getFullYear(); const hasBirthdayPassed = today.getMonth() > dateOfBirth.getMonth() || (today.getMonth() === dateOfBirth.getMonth() && today.getDate() >= dateOfBirth.getDate()); if (!hasBirthdayPassed) { age -= 1; } return age >= 0 ? age : null; } return null; } function getStoredAge() { try { const rawValue = window.localStorage.getItem( getQuestionStorageKey("personal_info"), ); if (!rawValue) { return null; } const storedAnswers = JSON.parse(rawValue) as StoredAnswers; const ageField = storedAnswers.fields?.find((field) => { const f = field as { key?: string; type?: string; label?: string }; return ( f.type === "number" || f.label === "Age" || f.label === "سن" || (typeof f.key === "string" && (f.key.endsWith("_age") || f.key.endsWith("_sn"))) ); }); if (ageField) { return parseStoredAge(ageField.value); } const dateOfBirthField = storedAnswers.fields?.find((field) => { const f = field as { key?: string; type?: string; label?: string }; return ( f.type === "date" || f.label === "Date of Birth" || f.label === "تاریخ تولد" || (typeof f.key === "string" && (f.key.endsWith("_date_of_birth") || f.key.endsWith("_tarykh_twld"))) ); }); return parseStoredAge(dateOfBirthField?.value); } catch { return null; } } function QuestionFlowWrapper({ visibleQuestions, itemSlug, dobQuestion, dobQuestionIndex, continueLabel, questionsListHref, }: { visibleQuestions: QuestionField[]; itemSlug: string; dobQuestion?: QuestionField; dobQuestionIndex?: number; requiredQuestionsCount: number; continueLabel: string; questionsListHref: string; }) { const { getAnswerValue } = useQuestionAnswers(); const dynamicQuestions = useMemo(() => { // Find employment status question by title or by choices length (10) const employmentQuestionIndex = visibleQuestions.findIndex((q) => { return ( q.title === "Employment Status" || q.title === "وضعیت اشتغال" || (q.type === "dropdown" && q.extras.options?.length === 10) ); }); let employmentSelectedOptionIndex = -1; if (employmentQuestionIndex !== -1) { const employmentQuestion = visibleQuestions[employmentQuestionIndex]; const ans = getAnswerValue(employmentQuestion, employmentQuestionIndex); if (ans) { employmentSelectedOptionIndex = employmentQuestion.extras.options?.indexOf(String(ans)) ?? -1; } } // Find Parents' Survival Status question index and check selected index const survivalStatusQuestionIndex = visibleQuestions.findIndex((q) => { return ( q.title === "Parents' Survival Status" || q.title === "وضعیت حیات والدین" ); }); let survivalSelectedOptionIndex = -1; if (survivalStatusQuestionIndex !== -1) { const survivalQuestion = visibleQuestions[survivalStatusQuestionIndex]; const ans = getAnswerValue(survivalQuestion, survivalStatusQuestionIndex); if (ans) { survivalSelectedOptionIndex = survivalQuestion.extras.options?.indexOf(String(ans)) ?? -1; } } // Find Parents' Marital Status question index and check selected index const maritalStatusQuestionIndex = visibleQuestions.findIndex((q) => { return ( q.title === "Parents' Marital Status" || q.title === "وضعیت تأهل والدین" ); }); let isCircumstancesSelected = false; if (maritalStatusQuestionIndex !== -1) { const maritalQuestion = visibleQuestions[maritalStatusQuestionIndex]; const ans = getAnswerValue(maritalQuestion, maritalStatusQuestionIndex); if (ans) { const selectedIdx = maritalQuestion.extras.options?.indexOf(String(ans)) ?? -1; isCircumstancesSelected = selectedIdx === 2; } } // Find Current Marital Status question index and check selected index const currentMaritalQuestionIndex = visibleQuestions.findIndex((q) => { return ( q.title === "Current Marital Status" || q.title === "وضعیت تأهل فعلی" ); }); let maritalSelectedIndex = -1; if (currentMaritalQuestionIndex !== -1) { const maritalQuestion = visibleQuestions[currentMaritalQuestionIndex]; const ans = getAnswerValue(maritalQuestion, currentMaritalQuestionIndex); if (ans) { maritalSelectedIndex = maritalQuestion.extras.options?.indexOf(String(ans)) ?? -1; } } // Find Children and Guardianship Status question index const custodyQuestionIndex = visibleQuestions.findIndex((q) => { return ( q.title === "Children and Guardianship Status" || q.title === "وضعیت فرزند و تکفل" ); }); let hasChildrenSelected = false; let hasAnyGuardianshipSelected = false; if (custodyQuestionIndex !== -1) { const custodyQuestion = visibleQuestions[custodyQuestionIndex]; const ans = getAnswerValue(custodyQuestion, custodyQuestionIndex); if (ans) { const ansList = Array.isArray(ans) ? ans.map(String) : [String(ans)]; hasChildrenSelected = ansList.some( (val) => val.includes("Have children") || val.includes("فرزند دارم"), ); hasAnyGuardianshipSelected = ansList.some( (val) => val.includes("Have children") || val.includes("فرزند دارم") || val.includes("under my guardianship") || val.includes("تحت تکفل"), ); } } const filtered = visibleQuestions.filter((question, index) => { // Check if this is one of the marital status/children questions if (currentMaritalQuestionIndex !== -1) { const isDuration = index === currentMaritalQuestionIndex + 1 || question.title === "Previous Marriage Duration" || question.title === "مدت ازدواج یا عقد قبلی"; const isSeparation = index === currentMaritalQuestionIndex + 2 || question.title === "Reason for Separation" || question.title === "علت جدایی، در صورت وجود"; const isCustody = index === currentMaritalQuestionIndex + 3 || question.title === "Children and Guardianship Status" || question.title === "وضعیت فرزند و تکفل"; const isChildrenCount = index === currentMaritalQuestionIndex + 4 || question.title === "Number of Children" || question.title === "تعداد فرزندان"; const isChildrenExplanation = index === currentMaritalQuestionIndex + 5 || question.title === "Short Children/Guardianship Explanation" || question.title === "توضیح کوتاه درباره شرایط فرزند یا تکفل"; if (isDuration) { return [1, 2, 3].includes(maritalSelectedIndex); } if (isSeparation) { return [1, 2].includes(maritalSelectedIndex); } if (isCustody) { return [2, 3].includes(maritalSelectedIndex); } if (isChildrenCount) { return [2, 3].includes(maritalSelectedIndex) && hasChildrenSelected; } if (isChildrenExplanation) { return ( [2, 3].includes(maritalSelectedIndex) && hasAnyGuardianshipSelected ); } } // Check if this is one of the three job-related questions if (employmentQuestionIndex !== -1) { const isJobTitle = index === employmentQuestionIndex + 1 || question.title === "Job Title" || question.title === "عنوان شغلی"; const isWorkLocation = index === employmentQuestionIndex + 2 || question.title === "Work Location" || question.title === "محل فعالیت"; const isMonthlyIncome = index === employmentQuestionIndex + 3 || question.title === "Monthly Income" || question.title === "میزان درآمد ماهانه"; if (isJobTitle || isWorkLocation || isMonthlyIncome) { // If no employment status is selected yet, hide them by default if (employmentSelectedOptionIndex === -1) { return false; } // Options logic: // Show all 3 for: index 0 (Full-time), 1 (Part-time), 2 (Self-employed), 3 (Entrepreneur), 5 (Working Student) const showAll = [0, 1, 2, 3, 5].includes( employmentSelectedOptionIndex, ); // Hide all 3 for: index 4 (Student), 6 (Student & Job Seeking), 7 (Job Seeking / Unemployed), 8 (Homemaker) const hideAll = [4, 6, 7, 8].includes(employmentSelectedOptionIndex); // Special Retired logic: index 9 (Retired) const isRetired = employmentSelectedOptionIndex === 9; if (showAll) { return true; } if (hideAll) { return false; } if (isRetired) { if (isJobTitle || isMonthlyIncome) { return true; } if (isWorkLocation) { return false; } } return false; } } // Check Parents' Survival Status to decide if Parents' Marital Status is visible if (survivalStatusQuestionIndex !== -1) { const isParentsMaritalStatus = question.title === "Parents' Marital Status" || question.title === "وضعیت تأهل والدین"; if (isParentsMaritalStatus) { return survivalSelectedOptionIndex === 0; } } // Default dependsOn logic if (question.logic?.dependsOn) { const { title, values } = question.logic.dependsOn; const dependentQuestionIndex = visibleQuestions.findIndex( (q) => q.title === title, ); if (dependentQuestionIndex !== -1) { const dependentQuestion = visibleQuestions[dependentQuestionIndex]; const answer = getAnswerValue( dependentQuestion, dependentQuestionIndex, ); if (Array.isArray(answer)) { return answer.some((ans) => values.includes(String(ans))); } return values.includes(String(answer)); } return false; } return true; }); return filtered.map((question) => { if ( question.title === "Short Family Description" || question.title === "توضیح کوتاه درباره خانواده" ) { return { ...question, required: isCircumstancesSelected, }; } if ( question.title === "Previous Marriage Duration" || question.title === "مدت ازدواج یا عقد قبلی" || question.title === "Number of Children" || question.title === "تعداد فرزندان" || question.title === "Short Children/Guardianship Explanation" || question.title === "توضیح کوتاه درباره شرایط فرزند یا تکفل" || question.title === "Additional details about family responsibility" || question.title === "توضیحات تکمیلی درباره مسئولیت خانوادگی" || question.title === "Do the supported individual(s) live with you?" || question.title === "آیا فرد یا افراد تحت حمایت با شما زندگی میکنند؟" || question.title === "What is the custody status of your child(ren)?" || question.title === "وضعیت حضانت فرزند یا فرزندان شما چگونه است؟" || question.title === "Does the custody, visitation, or relocation schedule impact your residence or immigration?" || question.title === "آیا برنامه حضانت، ملاقات یا جابهجایی فرزند بر محل زندگی یا امکان مهاجرت شما تأثیر میگذارد؟" || question.title === "What is the payment or receipt status of child support?" || question.title === "وضعیت پرداخت یا دریافت نفقه و حمایت مالی فرزند چگونه است؟" || question.title === "Acceptance of necessary communication between future spouse and the other parent" || question.title === "پذیرش ارتباط ضروری همسر آینده با والد دیگرِ فرزند" || question.title === "پذیرش ارتباط ضروری همسر آینده با والد دیگر فرزند" ) { return { ...question, required: true, }; } return question; }); }, [visibleQuestions, getAnswerValue]); const requiredCount = useMemo( () => dynamicQuestions.filter((q) => q.required).length, [dynamicQuestions], ); return ( question.required ? [] : [index], )} questions={dynamicQuestions} > {dynamicQuestions.map((question, index) => { let originalIndex = visibleQuestions.indexOf(question); if (originalIndex === -1) { // Spread-copied questions lose reference equality; fall back to title originalIndex = visibleQuestions.findIndex( (q) => q.title === question.title, ); } const answer = getAnswerValue(question, originalIndex); const hasAnswer = hasQuestionAnswerValue(answer ?? null); let isAnswered = hasAnswer; if (hasAnswer) { const isEmailQuestion = question.title.toLowerCase().includes("email") || question.title.includes("ایمیل"); if (isEmailQuestion) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; isAnswered = emailRegex.test(String(answer).trim()); } else if (question.type === "birthplace") { const strVal = String(answer); const parts = strVal.split(",").map((p) => p.trim()); isAnswered = parts.length >= 2 && parts[0].length > 0 && parts[1].length > 0; } else if (question.type === "checkbox") { isAnswered = Array.isArray(answer) ? answer.length > 0 : hasAnswer; } } return (
); })}
); } export default function QuestionDetailClient({ closeLabel, continueLabel, description, informationLabel, itemSlug, locale = defaultLocale, questionsListHref, title, }: QuestionDetailClientProps) { const router = useRouter(); const { dictionary: t } = useI18n(); const [isTestStarted, setIsTestStarted] = useState(false); const { data: profile, isLoading: isProfileLoading } = useMarriageProfileQuery(); const profileGender = profile?.gender; const age = getStoredAge(); const item = getQuestionListItemBySlug(itemSlug, locale); const isCattellSlug = itemSlug === "personality_test"; const isGlasserSlug = itemSlug === "glasser_5_needs_test"; const cattellQuery = useCattellQuestionsQuery(locale, { enabled: isCattellSlug && isTestStarted, retry: 0, }); const submitCattellMutation = useSubmitCattellAssessmentMutation(); const glasserQuery = useGlasserQuestionsQuery(locale, { enabled: isGlasserSlug && isTestStarted, retry: 0, }); const submitGlasserMutation = useSubmitGlasserAssessmentMutation(); const profileContext = useMemo( () => ({ age, gender: profileGender as MarriageGender | null | undefined, }), [age, profileGender], ); const cattellTestQuestions: TestQuestion[] = useMemo(() => { const questionsList = cattellQuery.data?.questions && cattellQuery.data.questions.length > 0 ? cattellQuery.data.questions : cattellFallbackQuestions; return questionsList.map((q) => { const rawOptions = q.options && q.options.length > 0 ? q.options : locale === "fa" ? ["بله", "به اندازه کافی واضح نیست", "نه"] : ["Yes", "Not clear enough", "No"]; const mappedOptions = rawOptions.map((optText, idx) => ({ label: optText, value: idx === 0 ? "A" : idx === 1 ? "B" : "C", })); return { id: q.question_number, text: q.text, options: mappedOptions, }; }); }, [cattellQuery.data, locale]); const glasserTestQuestions: TestQuestion[] = useMemo(() => { const questionsList = glasserQuery.data?.questions && glasserQuery.data.questions.length > 0 ? glasserQuery.data.questions : glasserFallbackQuestions; const defaultGlasserOptions = [ { label: locale === "fa" ? "خیلی کم (۱)" : "Very Low (1)", value: 1 }, { label: locale === "fa" ? "کم (۲)" : "Low (2)", value: 2 }, { label: locale === "fa" ? "متوسط (۳)" : "Moderate (3)", value: 3 }, { label: locale === "fa" ? "زیاد (۴)" : "High (4)", value: 4 }, { label: locale === "fa" ? "خیلی زیاد (۵)" : "Very High (5)", value: 5 }, ]; return questionsList.map((q) => ({ id: q.question_number, text: q.text, info: "factor" in q ? (q.factor as string) : "factor_code" in q ? (q.factor_code as string) : undefined, options: defaultGlasserOptions, })); }, [glasserQuery.data, locale]); const visibleQuestions = useMemo(() => { if (!item) { return []; } const hasDobQuestion = item.questions.some( (q) => q.title === "Date of Birth" || q.title === "تاریخ تولد", ); return item.questions .filter((question) => { if ( hasDobQuestion && (question.title === "Age" || question.title === "سن") ) { return false; } return isQuestionVisibleForProfile(question, profileContext); }) .map((question) => ({ ...question, required: isQuestionRequiredForProfile(question, profileContext), })); }, [item, profileContext]); const requiredQuestionsCount = useMemo( () => visibleQuestions.filter((q) => q.required).length, [visibleQuestions], ); useEffect(() => { if (isProfileLoading) { return; } if (!item || isQuestionListItemVisibleForProfile(item, profileContext)) { return; } router.replace(questionsListHref); }, [isProfileLoading, item, profileContext, questionsListHref, router]); if (isProfileLoading && item) { return ( <>
); } else if ( !item || !isQuestionListItemVisibleForProfile(item, profileContext) ) { return null; } if (item && item.questions.length === 0) { if (isTestStarted) { const isQuestionsLoading = isCattellSlug ? cattellQuery.isLoading || cattellQuery.isFetching : isGlasserSlug ? glasserQuery.isLoading || glasserQuery.isFetching : false; if (isQuestionsLoading) { return ( <>
); } const activeTestQuestions = isCattellSlug ? cattellTestQuestions : isGlasserSlug ? glasserTestQuestions : []; if (activeTestQuestions.length === 0) { const isError = isCattellSlug ? cattellQuery.isError : isGlasserSlug ? glasserQuery.isError : false; const refetch = isCattellSlug ? cattellQuery.refetch : glasserQuery.refetch; return ( <>

{isError ? locale === "fa" ? "خطا در دریافت سوالات از سرور. لطفاً از اتصال اینترنت یا ورود به حساب کاربری اطمینان حاصل کنید." : "Failed to load questions from server. Please check your connection or login status." : locale === "fa" ? "سوالاتی برای این آزمون یافت نشد." : "No questions found for this test."}

); } const handleTestFinish = async ( answers: Record, ) => { if (isCattellSlug) { const responses = Object.entries(answers).map(([qNum, option]) => ({ question_number: Number(qNum), option: String(option), })); try { await submitCattellMutation.mutateAsync({ responses }); } catch { // Ignore if already submitted or API returned error } try { window.localStorage.setItem( getQuestionStorageKey(item.slug), JSON.stringify({ completed: true }), ); } catch {} } else if (isGlasserSlug) { const responses = Object.entries(answers).map(([qNum, score]) => ({ question_number: Number(qNum), score: Number(score), })); try { await submitGlasserMutation.mutateAsync({ responses }); } catch { // Ignore if already submitted } try { window.localStorage.setItem( getQuestionStorageKey(item.slug), JSON.stringify({ completed: true }), ); } catch {} } await new Promise((resolve) => setTimeout(resolve, 1200)); }; return ( setIsTestStarted(false)} onFinish={handleTestFinish} /> ); } const bulletKey = item.slug === "glasser_5_needs_test" ? "glasser" : "personality"; const bullets = t.questions.testIntroBullets[bulletKey]; return ( <>

{item.title}

{ setIsTestStarted(true); }} />
); } const dobQuestion = visibleQuestions.find( (question) => question.title === "Date of Birth", ); const dobQuestionIndex = visibleQuestions.findIndex( (question) => question.title === "Date of Birth", ); return ( <>

{item.title}

); }